package com.jesse.custom.asyncimageview;

import java.io.IOException;
import java.io.InputStream;

import com.jesse.custom.asyncimageview.AsyncImageFileUtil.ImageType;
import com.jesse.main.R;
import com.jesse.util.ViewUtil;
import com.nostra13.universalimageloader.core.assist.ImageScaleType;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.Shader;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.View;
import android.view.animation.AlphaAnimation;
import android.widget.ImageView;

/**
 * 带有内存缓存，和SD卡缓存与缩放功能的异步加载图片资源的ImageView，适合显示网络图片，自带资源图片。
 * 
 * 其内存缓存方式为LruCache，其中核心类为{@link AsyncImageLoader},该类提供了缓存操作。
 * 
 * 而图片的缩放由缩放类{@link AsyncImageScale}提供
 * 
 * @author jesse
 */
public class AsyncImageView extends ImageView{

	private Context context;
	private static final Bitmap.Config BITMAP_CONFIG = Bitmap.Config.RGB_565;
	private static final int COLORDRAWABLE_DIMENSION = 1;

	private static final int DEFAULT_BORDER_WIDTH = 0;
	private static final int DEFAULT_BORDER_COLOR = Color.BLACK;
	private static final boolean DEFAULT_CIRCLE = false;

	private final RectF mDrawableRect = new RectF();
	private final RectF mBorderRect = new RectF();

	private final Matrix mShaderMatrix = new Matrix();
	private final Paint mBitmapPaint = new Paint();
	private final Paint mBorderPaint = new Paint();

	private int mBorderColor = DEFAULT_BORDER_COLOR;
	private int mBorderWidth = DEFAULT_BORDER_WIDTH;
    private boolean isCircleImage = DEFAULT_CIRCLE;					//是否使用圆形View
    
	private Bitmap mBitmap;
	private BitmapShader mBitmapShader;
	private int mBitmapWidth;
	private int mBitmapHeight;

	private float mDrawableRadius;
	private float mBorderRadius;

	private boolean mReady;
	private boolean mSetupPending;
	
	/**
	 * 图片读取核心方法
	 */
	private AsyncImageLoader asyncImageLoader;
	
	/**
	 * 图片读取进度回调
	 */
	private AsyncImageviewSetviewComplete asyncImageviewSetviewCompleteDao;
	
	private int resId;
	
	private int resAlphaId;
	
	/**
	 * 图片下载地址
	 */
	private String url;
	
	/**
	 * Imageview所加载图片的宽度
	 */
	private int imageviewWidthwidh;
	
	/**
	 * Imageview所加载图片的高度
	 */
	private int imageviewHeight;
	
	/**
	 * 自定义传递信息
	 */
	private Object tagObject;
	
	public <T extends Object> T getTagObject() {
		return (T) tagObject;
	}

	public void setTagObject(Object tag) {
		this.tagObject = tag;
	}

	/**
	 * 等待，加载中默认显示图片
	 */
	private int loadingImageResId;
	
	public void setLoadingImage(int rId) {
		this.loadingImageResId = rId;
	}

	public AsyncImageView(Context context) {
		this(context, null);  
	}
	
	public AsyncImageView(Context context, AttributeSet attr) {
		super(context, attr);
		this.context = context;
		asyncImageLoader = AsyncImageLoader.getInstance();
		readXMLAttribute(context, attr);
	}
	
	private void readXMLAttribute(Context context, AttributeSet attrs) {
		TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.AsyncImageView);
	    mBorderWidth = array.getDimensionPixelSize(R.styleable.AsyncImageView_border_width, DEFAULT_BORDER_WIDTH);
	    mBorderColor = array.getColor(R.styleable.AsyncImageView_border_color, DEFAULT_BORDER_COLOR);
	    isCircleImage = array.getBoolean(R.styleable.AsyncImageView_circle, DEFAULT_CIRCLE);
	    resId = array.getResourceId(R.styleable.AsyncImageView_resId, 0);
		resAlphaId = array.getResourceId(R.styleable.AsyncImageView_resAlphaId, 0);
	    array.recycle();
	    mReady = true;
		if (resId != 0) {
			setBigImageResource(resId);
		}
		if (resAlphaId != 0) {
			setBigAlphaImageResource(resAlphaId);
		}
	    if (mSetupPending && isCircleImage) {
	      setup();
	      setScaleType(ScaleType.CENTER_CROP);
	      mSetupPending = false;
	    }
	}
	/**
	 * 设置读取进度回调{@link AsyncImageviewSetviewComplete}
	 * 1.onError
	 * 2.onLoading
	 * 3.onComplete
	 * @param asyncImageviewSetviewCompleteDao
	 */
	public void setAsyncImageviewSetviewCompleteDao(
			AsyncImageviewSetviewComplete asyncImageviewSetviewCompleteDao) {
		this.asyncImageviewSetviewCompleteDao = asyncImageviewSetviewCompleteDao;
	}

	/**
	 * 根据图片地址下载图片
	 * @param url	图片地址
	 * @param iconKey	该图片的索引key（推荐使用地址+图片宽高组成的MD5值）
	 * @return 返回下载好的图片资源，当下载完成会自动setImage，该返回值是用于其他操作。
	 */
	public Drawable getIconWithUrl(String url, String iconKey) {
		return getIconWithUrlScale(url, iconKey, 0, 0);
	}
	
	/**
	 * 根据图片地址下载图片,并且以宽度值为等比缩放
	 * @param url	图片地址
	 * @param iconKey	该图片的索引key（推荐使用地址+图片宽高组成的MD5值）
	 * @param width		想指定的图片宽度
	 * @return	返回下载并缩放过的图片资源，当下载完成会自动setImage，该返回值是用于其他操作。
	 */
	public Drawable getIconWithUrlScaleByWidth(String url, String iconKey, int width) {
		Drawable drawable = getIconWithUrlScale(url, iconKey, width, 0);
		return drawable;
	}
	
	/**
	 * 根据图片地址下载图片,并且以高度值为等比缩放
	 * @param url	图片地址
	 * @param iconKey	该图片的索引key（推荐使用地址+图片宽高组成的MD5值）
	 * @param hight		想指定的图片高度
	 * @return		返回下载并缩放过的图片资源，当下载完成会自动setImage，该返回值是用于其他操作。
	 */
	public Drawable getIconWithUrlScaleByHight(String url, String iconKey, int hight) {
		Drawable drawable = getIconWithUrlScale(url, iconKey, 0, hight);
		return drawable;
	}
	
	/**
	 * 根据图片地址下载图片,并且指定宽高缩放，非等比缩放
	 * @param url	图片地址
	 * @param iconKey	该图片的索引key（推荐使用地址+图片宽高组成的MD5值）
	 * @param width		想指定的图片宽度
	 * @param hight		想指定的图片高度
	 * @return		返回下载并缩放过的图片资源，当下载完成会自动setImage，该返回值是用于其他操作。	
	 */
	public Drawable getIconWithUrlScale(String url, String iconKey, int width, int hight) {
		Drawable drawable = getDrawable(url, iconKey, width, hight);
		return drawable;
	}
	
	/**
	 * 下载图片，设置图片核心方法。
	 * 
	 * 1.当下载地址为空时，自动显示图片为下载失败图片
	 * 
	 * 2.当下载中时，自动显示图片为Loading
	 * 
	 * 3.下载完成后，自动显示为缩放后，或未经缩放的图片。
	 * 
	 * @param url
	 * @param iconKey
	 * @param width
	 * @param hight
	 * @return	返回最终结果，或下载中的loading图片。
	 */
	private Drawable getDrawable (String url, final String iconKey, final int width, final int hight) {
		if (url.isEmpty() && iconKey.isEmpty()) {
			if (asyncImageviewSetviewCompleteDao != null) {
				asyncImageviewSetviewCompleteDao.onError(this.getDrawable(), tagObject);
			}
			return this.getDrawable();
		}
		
		/**
		 * 图片下载
		 */
		asyncImageLoader.loadDrawable(url, iconKey, width, hight, new AsynImageDownloadDao() {
			
			@Override
			public void imageLoaded(Bitmap imageBitmap, String imageUrl, Object msg) {
				if (imageBitmap == null && (Integer)msg == -1) {
//					setImage(transforLoadingIconKey(iconKey), loadingImageResId, width, hight); 
					if (asyncImageviewSetviewCompleteDao != null) {
						asyncImageviewSetviewCompleteDao.onPrepare(AsyncImageView.this.getDrawable(), tagObject);
					}
				}
				if (imageBitmap == null && (Integer)msg >= 0) {
					if (asyncImageviewSetviewCompleteDao != null) {
						asyncImageviewSetviewCompleteDao.onLoading(AsyncImageView.this.getDrawable(), (Integer)msg, tagObject);
					}
				} 
				if (imageBitmap != null) {
					setImage(iconKey, imageBitmap, width, hight); 
					setImageviewWidth(imageBitmap.getWidth());
					setImageviewHeight(imageBitmap.getHeight());
					if (asyncImageviewSetviewCompleteDao != null) {
						asyncImageviewSetviewCompleteDao.onComplete(imageBitmap, imageBitmap.getWidth(), imageBitmap.getHeight(), tagObject);
					}
				}
			}
		});
		return this.getDrawable();
	}

	private void setImage(String iconKey, int id, int width, int hight) {
		setImage(iconKey, context.getResources().getDrawable(id), width, hight);
	}
	
	private void setImage(String iconKey, Drawable drawable, int width, int hight) {
		setImage(iconKey, ((BitmapDrawable)drawable).getBitmap(), width, hight);
	}
	
	/**
	 * 用于图片缩放的方法，其中{@link AsyncImageScale} 是核心类。
	 * @param iconKey
	 * @param bmp
	 * @param width
	 * @param hight
	 */
	private void setImage(String iconKey, Bitmap bmp, int width, int hight) {
		AsyncImageScale imageScale = new AsyncImageScale(iconKey, bmp, width, hight);
		//TODO 暂停使用sclae功能，会导致内存溢出，返回值为原Bitmap
		Bitmap newBitmap = imageScale.scaleToBitmap();
		setImageBitmap(newBitmap);
	}
	
	/**
	 * 对于Loading图片，也将其放入到缓存中，其默认key值有变
	 * @param iconKey
	 * @return
	 */
	private String transforLoadingIconKey(String iconKey) {
		return iconKey+"loading";
	}
	
	/**
	 * 下载失败的图片，也放入缓存，其默认key值有变
	 * @param iconKey
	 * @return
	 */
	private String transforErrorIconKey(String iconKey) {
		return iconKey+"error";
	}
	
	public String getUrl() {
		return url;
	}

	public void setUrl(String url) {
		this.url = url;
	}
	
	public int getImageviewWidthwidh() {
		return imageviewWidthwidh;
	}

	private void setImageviewWidth(int imageviewWidthwidh) {
		this.imageviewWidthwidh = imageviewWidthwidh;
	}

	public int getImageviewHeight() {
		return imageviewHeight;
	}

	private void setImageviewHeight(int imageviewHeight) {
		this.imageviewHeight = imageviewHeight;
	}
	
	public int getBorderColor() {
	    return mBorderColor;
	}

	public boolean isCircleImage() {
		return isCircleImage;
	}

	public void setCircleImage(boolean isCircleImage) {
		this.isCircleImage = isCircleImage;
	}

	public void setBorderColor(int borderColor) {
	    if (borderColor == mBorderColor) {
	      return;
	    }

	    mBorderColor = borderColor;
	    mBorderPaint.setColor(mBorderColor);
	    invalidate();
	}

	public int getBorderWidth() {
	    return mBorderWidth;
	}

	public void setBorderWidth(int borderWidth) {
	    if (borderWidth == mBorderWidth) {
	      return;
	    }
	    mBorderWidth = borderWidth;
	    setup();
	}

	@Override  
    protected void onDraw(Canvas canvas) {  
		if (canvas == null) return;
		if (isCircleImage) {
			canvas.drawCircle(getWidth() / 2, getHeight() / 2, mDrawableRadius, mBitmapPaint);
		    canvas.drawCircle(getWidth() / 2, getHeight() / 2, mBorderRadius, mBorderPaint);
		    return;
		}
		
        try {  
            super.onDraw(canvas);  
        } catch (Exception e) {  
            ViewUtil.Debug("MyImageView  -> onDraw() Canvas: trying to use a recycled bitmap");  
        }  
    }  
	
	public static void recycleImageView(ImageView view) {
		if (view.getDrawable() != null && view.getDrawable() instanceof BitmapDrawable){
			Bitmap oldBitmap = ((BitmapDrawable)view.getDrawable()).getBitmap();
			if (oldBitmap != null && !oldBitmap.isRecycled()) {
				oldBitmap.recycle();
				oldBitmap = null;
			}
		}
		view = null;
	}
	
	@Override
	protected void onSizeChanged(int w, int h, int oldw, int oldh) {
	    super.onSizeChanged(w, h, oldw, oldh);
	    setup();
	}
	
	@Override
	public void setImageBitmap(Bitmap bm) {
	    super.setImageBitmap(bm);
	    if (!isCircleImage) return;
	    mBitmap = bm;
	    setup();
	}

	@Override
	public void setImageDrawable(Drawable drawable) {
	    super.setImageDrawable(drawable);
	    if (!isCircleImage) return;
	    mBitmap = getBitmapFromDrawable(drawable);
	    setup();
	}

	@Override
	public void setImageResource(int resId) {
	    super.setImageResource(resId);
	    if (!isCircleImage) return;
	    mBitmap = getBitmapFromDrawable(getDrawable());
	    setup();
	}
	
	/**
	 * 解决高清本地图占用内存过大
	 * 不带alpha通道的图
	 * @param resId
	 */
	public void setBigImageResource(int resId) {
		setMiniImageResource(Bitmap.Config.RGB_565, resId);
	}
	
	/**
	 * 解决高清本地图占用内存过大
	 * 带alpha通道的图
	 * @param resId
	 */
	public void setBigAlphaImageResource(int resId) {
		setMiniImageResource(Bitmap.Config.ARGB_8888, resId);
	}
	
	/**
	 * 减小图片尺寸
	 * @param config
	 * @param resId
	 */
	private void setMiniImageResource(Bitmap.Config config, int resId) {
		if (resId == 0) return;
		
		if (AsyncImageLoader.getInstance().getBitmapFromMemoryCache(resId + "") != null) {
			setScaleType(ScaleType.CENTER_CROP);
			setImageBitmap(AsyncImageLoader.getInstance().getBitmapFromMemoryCache(resId + ""));
			return;
		}
		
		BitmapFactory.Options opt = new BitmapFactory.Options();
		opt.inPreferredConfig = config;
		opt.inPurgeable = true;
		opt.inInputShareable = true;
		InputStream is = this.getResources().openRawResource(resId);
		Bitmap bitmap = BitmapFactory.decodeStream(is, null, opt);
		setScaleType(ScaleType.CENTER_CROP);
		setImageBitmap(bitmap);
		try {
			is.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
		if (bitmap == null) return;
		AsyncImageLoader.getInstance().addBitmapToMemoryCache(resId + "", bitmap);
	}
	
	private Bitmap getBitmapFromDrawable(Drawable drawable) {
	    if (drawable == null) {
	      return null;
	    }

	    if (drawable instanceof BitmapDrawable) {
	      return ((BitmapDrawable) drawable).getBitmap();
	    }

	    try {
	      Bitmap bitmap;

	      if (drawable instanceof ColorDrawable) {
	        bitmap = Bitmap.createBitmap(COLORDRAWABLE_DIMENSION, COLORDRAWABLE_DIMENSION, BITMAP_CONFIG);
	      } else {
	        bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), BITMAP_CONFIG);
	      }

	      Canvas canvas = new Canvas(bitmap);
	      drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
	      drawable.draw(canvas);
	      return bitmap;
	    } catch (OutOfMemoryError e) {
	      return null;
	    }
	}
	
	private void setup() {
		if (!isCircleImage) return;
		
	    if (!mReady) {
	      mSetupPending = true;
	      return;
	    }

	    if (mBitmap == null) {
	      return;
	    }

	    mBitmapShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);

	    mBitmapPaint.setAntiAlias(true);
	    mBitmapPaint.setShader(mBitmapShader);

	    mBorderPaint.setStyle(Paint.Style.STROKE);
	    mBorderPaint.setAntiAlias(true);
	    mBorderPaint.setColor(mBorderColor);
	    mBorderPaint.setStrokeWidth(mBorderWidth);

	    mBitmapHeight = mBitmap.getHeight();
	    mBitmapWidth = mBitmap.getWidth();

	    mBorderRect.set(0, 0, getWidth(), getHeight());
	    mBorderRadius = Math.min((mBorderRect.height() - mBorderWidth) / 2, (mBorderRect.width() - mBorderWidth) / 2);

	    mDrawableRect.set(mBorderWidth, mBorderWidth, mBorderRect.width() - mBorderWidth, mBorderRect.height() - mBorderWidth);
	    mDrawableRadius = Math.min(mDrawableRect.height() / 2, mDrawableRect.width() / 2);
	    updateShaderMatrix();
	    invalidate();
	  }
	
	private void updateShaderMatrix() {
	    float scale;
	    float dx = 0;
	    float dy = 0;

	    mShaderMatrix.set(null);

	    if (mBitmapWidth * mDrawableRect.height() > mDrawableRect.width() * mBitmapHeight) {
	      scale = mDrawableRect.height() / (float) mBitmapHeight;
	      dx = (mDrawableRect.width() - mBitmapWidth * scale) * 0.5f;
	    } else {
	      scale = mDrawableRect.width() / (float) mBitmapWidth;
	      dy = (mDrawableRect.height() - mBitmapHeight * scale) * 0.5f;
	    }

	    mShaderMatrix.setScale(scale, scale);
	    mShaderMatrix.postTranslate((int) (dx + 0.5f) + mBorderWidth, (int) (dy + 0.5f) + mBorderWidth);

	    mBitmapShader.setLocalMatrix(mShaderMatrix);
	  }
	
	@Override
	protected void onDetachedFromWindow() {
		mBitmap = null;
		mBitmapShader = null;
		setImageBitmap(null);
		if(getBackground() != null)
			getBackground().setCallback(null);
		super.onDetachedFromWindow();
	}
}
